/*
 * OpenGL tools - Render to texture example code.
 * 
 * MIT License
 * 
 * Copyright (c) 2010, Markus Broecker <mbrckr@gmail.com>
 * 
 * Permission is hereby granted, free of charge, to any person obtaining a copy
 * of this software and associated documentation files (the "Software"), to deal
 * in the Software without restriction, including without limitation the rights
 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 * copies of the Software, and to permit persons to whom the Software is
 * furnished to do so, subject to the following conditions:
 * 
 * The above copyright notice and this permission notice shall be included in
 * all copies or substantial portions of the Software.
 * 
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
 * THE SOFTWARE.
 */

#include <Framebuffer.h>
#include <OpenGL.h>

#include <iostream>
#include <cstdlib>

using namespace glTools;

static int gWindow = 0;
static Framebuffer* gRenderTarget = 0;

static unsigned int gTimestamp = 0;
static float gRotation = 0.f;


/** Renders a scene into the render target for later texture use.
*/
static void renderScene()
{
	// Bind the framebuffer and clear it.
	gRenderTarget->bind();
	glClearColor(0, 0, 0.2, 1);
	glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
	
	// setup the camera
	glMatrixMode(GL_PROJECTION);
	glLoadIdentity();
	gluPerspective(60.0, 1.33, 1.0, 100.0);
	
	glMatrixMode(GL_MODELVIEW);
	glLoadIdentity();
	gluLookAt(0, 5, 10, 0, 0, 0, 0, 1, 0);
	
	// Enable some lighting to make it more interesting
	glEnable(GL_LIGHTING);
	glEnable(GL_LIGHT0);
	float light_pos[] = {10, 10, 0, 0};
	glLightfv(GL_LIGHT0, GL_POSITION, light_pos);
	glEnable(GL_COLOR_MATERIAL);
	
	glRotatef(-gRotation, 0.1, 1, 0.4);
	
	// render a teapot
	glFrontFace(GL_CW);
	glColor3f(1, 0.2, 0);
	glutSolidTeapot(3.f);
	glFrontFace(GL_CCW);
	
	glDisable(GL_LIGHTING);
	
	/* 	Note that you have to disable the bound framebuffer, as it will 
		restore viewport coordinates.
	*/	
	gRenderTarget->disable();
}

/**	Renders a textured, rotating cube.
*/
static void renderTexturedCube()
{
	
	// Setup projection and view matrix
	glMatrixMode(GL_PROJECTION);
	glLoadIdentity();
	gluPerspective(60.0, 1.33, 1.0, 100.0);
	
	glMatrixMode(GL_MODELVIEW);
	glLoadIdentity();
	gluLookAt(0, 1, 15, 0, 0, 0, 0, 1, 0);
	
	glRotatef(gRotation, 0, 1, 0);
	
	// Enable texturing and bind the first (and here only) colorbuffer
	glEnable(GL_TEXTURE_2D);
	glBindTexture(GL_TEXTURE_2D, gRenderTarget->getColorbuffer(0));
	
	// Render 4 cube sides
	for (int i = 0; i < 4; ++i)
	{
		glRotatef(90, 0, 1, 0);
		glPushMatrix();
		glTranslatef(0, 0, 4);
	
		// one cube side
		glColor3f(1, 1, 1);
		glBegin(GL_QUADS);
		glTexCoord2i(0, 1);
		glVertex2i(-4, 3);
		glTexCoord2i(0, 0);
		glVertex2i(-4, -3);
		glTexCoord2i(1, 0);
		glVertex2i(4, -3);
		glTexCoord2i(1, 1);
		glVertex2i(4, 3);
		glEnd();
		
		glPopMatrix();
	}
	
	glDisable(GL_TEXTURE_2D);
}


static void display()
{
	renderScene();
	
	// Clear the window
	glClearColor(0.3, 0, 0, 1);
	glClear(GL_COLOR_BUFFER_BIT|GL_DEPTH_BUFFER_BIT);

	renderTexturedCube();
	
	glError();
	glutSwapBuffers();
}

static void keyboard(unsigned char key, int x, int y)
{
	// exit on escape
	if (key == 27)
	{
		delete gRenderTarget;
		exit(0);
	}
}

static void idle()
{
	// get the time to compute the delta - fractions of a second that passed
	unsigned int time = glutGet(GLUT_ELAPSED_TIME);
	float delta = static_cast<float>(time - gTimestamp) / 1000.f;
	gTimestamp = time;
	
	// Add some animation to the scene
	gRotation += delta * 10.0;
	
	// request a new frame
	glutPostRedisplay();
}

int main(int argc, const char** argv)
{
	// init glut and set the parameters
	glutInit( &argc, const_cast<char**>(argv));
	glutInitDisplayMode(GLUT_RGBA|GLUT_DEPTH|GLUT_DOUBLE);
	glutInitWindowSize(800, 600);
	
	// create the window 
	gWindow = glutCreateWindow("shader example");
	
	#ifndef __APPLE__
		glewInit();
	#endif
	
	// register the callback functions
	glutDisplayFunc(display);
	glutKeyboardFunc(keyboard);
	glutIdleFunc(idle);
	
	glEnable(GL_DEPTH_TEST);
	
	// Create a framebuffer with RGB channels and 1 texture to write to.
	gRenderTarget = new Framebuffer(512, 512);
	
	// enter the main loop
	glutMainLoop();
	return 0;
}